package org.oos.atom;

import static org.oos.atom.AtomResponse.responseBuilder;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.oos.atom.AtomResponse.Builder;

import com.google.api.client.http.HttpResponse;

/**
 * Provides execution methods to do REST interaction with Atom feeds
 * 
 * @author pablo.gonzalez@11870.com
 * 
 */
public final class AtomService {

	// static {
	// HttpTransport.setLowLevelHttpTransport(AndroidHttp.newCompatibleTransport());
	// }

	// private static Logger log = OosLogger.getOOSLogger();
	//
	// private static Map<String, Object> cache = new WeakHashMap<String,
	// Object>();

	// /**
	// *
	// * Enumeration used the determine the methods to be used.
	// *
	// * @author pablo.gonzalez@11870.com
	// *
	// */
	// private static enum Method {
	// Get(GET), Post(POST), Delete(DELETE), Put(PUT);
	//
	// private HttpMethod httpMethod;
	//
	// private Method(HttpMethod method) {
	// this.httpMethod = method;
	// }
	//
	// public void log(GenericUrl url) {
	// log.info(name() + ": " + url.toString());
	// }
	//
	// public void logCached(GenericUrl url) {
	// log.info("Cached: " + name() + ": " + url.toString());
	// }
	//
	// public HttpResponse execute(AuthenticationHandler authHandler,
	// HttpTransport transport, GenericUrl url) throws IOException,
	// HttpStatusException {
	// return execute(authHandler, transport, url, null, null);
	// }
	//
	// /**
	// *
	// * Same as execute(HttpTransport, GenericUrl, Object) but without the
	// * data object.
	// *
	// */
	// public HttpResponse execute(AuthenticationHandler authHandler,
	// HttpTransport transport, GenericUrl url,
	// List<OptionsMap> parameters) throws IOException,
	// HttpStatusException {
	// return execute(authHandler, transport, url, null, parameters);
	// }
	//
	// /**
	// *
	// * Creates a new {@link HttpRequest} applies current method, URL and (if
	// * provided) a data object. Finally returns the {@link HttpResponse}
	// * from the created request.
	// *
	// * @param transport
	// * An {@link HttpTransport} to initiate the request.
	// * @param url
	// * a {@link GenericUrl} to use for the requests
	// * @param data
	// * Key/Value object (see google-api-client)
	// * @return the resultant {@link HttpResponse}
	// * @throws IOException
	// * @throws HttpStatusException
	// */
	// public HttpResponse execute(AuthenticationHandler authHandler,
	// HttpTransport transport, GenericUrl url, HttpContent content,
	// List<OptionsMap> parameters) throws IOException,
	// HttpStatusException {
	// return authHandler.getResponse(transport, url, httpMethod, content,
	// parameters, null);
	// }
	//
	// }
	
	private static AtomService atomService;
	
	public static AtomService atomService() {
		if (atomService == null) {
			atomService = new AtomService();
		}
		return atomService;
	}
	
	/**
	 * Removes the current singleton for AtomService
	 */
	public void delete() {
		atomService = null;
	}
	
	private AtomService() {
	}

	private Map<String, AtomResponse<?>> responseCache = Collections
			.synchronizedMap(new HashMap<String, AtomResponse<?>>());

	public <Entity extends AtomEntity> AtomResponse<Entity> execute(AtomRequest request) {
		return execute(request, null);
	}

	@SuppressWarnings("unchecked")
	public <Entity extends AtomEntity> AtomResponse<Entity> execute(AtomRequest request, Class<Entity> clazz) {

		AtomResponse<Entity> result = null;
		final String urlKey = request.getUrl().build();
		// check if the response has been cached an with the same type
		AtomResponse<?> candidate = null;
		synchronized (responseCache) {
			if (responseCache.containsKey(request.getUrl())) {
				candidate = responseCache.get(urlKey);
			}
		}
		if (candidate != null && clazz != null
				&& clazz.isAssignableFrom(candidate.getResult().getClass())) {
			result = (AtomResponse<Entity>) candidate;
		}
		if (result == null) {
			HttpResponse httpResponse;
			Builder<Entity> builder = responseBuilder();
			try {
				httpResponse = request.getHttpResponse();
				result = builder.setHttpResponse(httpResponse).build(clazz);
				synchronized (responseCache) {
					responseCache.put(urlKey, result);
				}
			} catch (Exception e) {
				result = builder.setThrowable(e).build(clazz);
			}
		}
		return result;
	}

	// /**
	// *
	// * Removes the item located in the provided {@link GenericUrl}.
	// *
	// * @param transport
	// * @param url
	// * @return
	// * @throws IOException
	// * @throws HttpStatusException
	// */
	// public synchronized static boolean executeDelete(
	// AuthenticationHandler authHandler, HttpTransport transport,
	// GenericUrl url) throws IOException, HttpStatusException {
	// HttpResponse response = Method.Delete.execute(authHandler, transport,
	// url);
	// Method.Delete.log(url);
	// cache.remove(url.toString());
	// return response.isSuccessStatusCode();
	// }
	//
	// /**
	// *
	// * This method is used to edit the data contained in the item located at
	// the
	// * provided {@link GenericUrl}
	// *
	// * @param transport
	// * @param url
	// * @param data
	// * @return
	// * @throws IOException
	// * @throws HttpStatusException
	// */
	// public synchronized static boolean executePut(
	// AuthenticationHandler authHandler, HttpTransport transport,
	// GenericUrl url, HttpContent content) throws IOException,
	// HttpStatusException {
	// HttpResponse response = Method.Put.execute(authHandler, transport, url,
	// content, null);
	// Method.Put.log(url);
	// cache.remove(url.toString());
	// return response.isSuccessStatusCode();
	// }

	/**
	 * 
	 * This exception is used to inform of an error with the request. Normally
	 * when the status code of the request is >=300.
	 * 
	 * @author pablo.gonzalez@11870.com
	 * 
	 */
	public static class HttpStatusException extends Exception {

		private static final long serialVersionUID = -1570038483509158408L;

		private int statusCode;

		private HttpResponse response;

		public HttpStatusException(int statusCode) {
			this(statusCode, null);
		}

		public HttpStatusException(int statusCode, String extraMessage) {
			super(((extraMessage != null) ? extraMessage : Integer
					.toString(statusCode)));
			this.statusCode = statusCode;
		}

		public int getStatusCode() {
			return statusCode;
		}

		public void setResponse(HttpResponse response) {
			this.response = response;
		}

		public HttpResponse getResponse() {
			return response;
		}

	}

}
